home *** CD-ROM | disk | FTP | other *** search
- Subject: v25i029: ile - an input line editor
- Newsgroups: comp.sources.unix
- Approved: vixie@pa.dec.com
-
- Submitted-By: Robert C. Pendleton <bobp@hal.com>
- Posting-Number: Volume 25, Issue 29
- Archive-Name: ile
-
- [ This program gives you interactive line editing similar to tcsh or ksh,
- except that it will run any program as its subprocess, so although you
- don't get to carry the `history' context from program to program, you
- can get fancy editing in programs that don't neccessarily support it.
- Running "ile /bin/csh" is a lot like tcsh, and "ile /bin/sh" is a lot
- like ksh. I wrote the Makefile. --vix ]
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 1 (of 1)."
- # Contents: MANIFEST Makefile README ile.1 ile.c
- # Wrapped by vixie@cognition.pa.dec.com on Fri Dec 13 14:38:22 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'MANIFEST' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'MANIFEST'\"
- else
- echo shar: Extracting \"'MANIFEST'\" \(269 characters\)
- sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
- X File Name Archive # Description
- X-----------------------------------------------------------
- X MANIFEST 1 This shipping list
- X Makefile 1
- X README 1
- X ile.1 1
- X ile.c 1
- END_OF_FILE
- if test 269 -ne `wc -c <'MANIFEST'`; then
- echo shar: \"'MANIFEST'\" unpacked with wrong size!
- fi
- # end of 'MANIFEST'
- fi
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(223 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- X# vix 13dec91
- X
- DESTPATH = /usr/local
- CFLAGS = -g
- CC = cc
- X
- all: ile
- X
- clean:
- X -rm *.o
- X -rm ile
- X
- install:
- X install -c ile $(DESTPATH)/bin/ile
- X install -c ile.1 $(DESTPATH)/man/man1/ile.1
- X
- ile: ile.o
- X cc -o ile ile.o -ltermcap
- END_OF_FILE
- if test 223 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- fi
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(250 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- NAME
- X ile - An input line editor for UNIX (Input Line Editor)
- X
- SYNTAX
- X ile [-file/name] [prog arg1 arg2 ... argn]
- X
- DESCRIPTION
- X The ile program is an input line editor that provides an
- X easier to use history mechanism than the shell.
- END_OF_FILE
- if test 250 -ne `wc -c <'README'`; then
- echo shar: \"'README'\" unpacked with wrong size!
- fi
- # end of 'README'
- fi
- if test -f 'ile.1' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'ile.1'\"
- else
- echo shar: Extracting \"'ile.1'\" \(16441 characters\)
- sed "s/^X//" >'ile.1' <<'END_OF_FILE'
- X.de EX \"Begin example
- X.ne 5
- X.if n .sp 1
- X.if t .sp .5
- X.nf
- X.in +.5i
- X..
- X.de EE
- X.fi
- X.in -.5i
- X.if n .sp 1
- X.if t .sp .5
- X..
- X.TH ILE 1 "5 May 1988"
- X.SH NAME
- X.PP
- ile - An input line editor for UNIX (Input Line Editor)
- X.PP
- X.SH SYNTAX
- X.PP
- X\fBile\fP [-file/name\fP] \fP[prog arg1 arg2 ... argn\fP]
- X.PP
- X.SH DESCRIPTION
- X.PP
- X
- The \fIile\fP program is an input line editor that provides an easier
- to use history mechanism than the shell.
- X
- X.PP
- X
- The \fIile\fP program can be run as a simple shell around any program.
- It gives any program an input line editing and a history mechanism. It
- can also be run around your favorite shell. When run around the shell
- X\fIile\fP records the input to programs as well as input to the shell
- in its history buffer.
- X
- X\fIile\fP takes two optional command line arguments. The first
- argument is the name of an initialization file containing user defined
- key and delimiter bindings. The second argument is the name of a
- program to execute and the command line arguments for that program.
- X
- X.PP
- X
- If no initialization file is given on the command line \fIile\fP first
- looks in \fI./.ilerc\fP and then in \fI$HOME/.ilerc\fP. If no
- initialization file is found \fIile\fP provides default values for
- delimiter and key bindings.
- X
- X.PP
- X
- If no program name is given on the command line \fIile\fP executes
- X\fIcsh\fp.
- X
- X.PP
- X.SH DEFAULT BINDINGS
- X.PP
- X
- Not everyone wants to have to figure out yet another initialization
- file format so \fIile\fP provides a complete set of default bindings
- for all its operations.
- X
- X.PP
- X.SH Delimiters
- X.PP
- X
- Delimiters are used in \fIile\fP to mark the beginnings and ends of
- words for the \fBforward_word\fP, \fBbackward_word\fP, and
- X\fBdelete_word\fP actions. The default delimiters are ' ' (blank), '/'
- X(slash), '.' (period), and '-' (dash). These were chosen because the
- author decided they were "natural" stopping characters in a UNIX
- environment.
- X
- X.PP
- X.SH Keys
- X.PP
- X
- The following table shows the default bindings of keys and key
- sequences provided by \fIile\fP. These are based on the emacs key
- bindings for similar operations.
- X
- X.EX 0
- X
- X^A - start_of_line
- X^B - backward_char
- X^E - end_of_line
- X^F - forward_char
- X^K - erase_to_end_of_line
- X^L - retype_line
- X^N - forward_history
- X^P - backward_history
- X^R - search_backward_history
- X^V - quote
- X^T - transpose_chars
- del - delete_char
- X^M - add_to_history
- X^J - add_to_history
- X^U - erase_line
- X^X - delete_char_under
- X
- X^C - pass
- X^D - pass
- X^Q - pass
- X^S - pass
- X^Z - pass
- X
- esc b - backward_word
- esc f - forward_word
- esc del - delete_word
- esc esc - complete_file
- esc s - complete_file_full
- esc p - query_path
- esc d - show_files
- esc u - upper_word
- esc l - lower_word
- esc c - capitalize_word
- X
- esc [ A - backward_history (up arrow)
- esc [ B - forward_history (down arrow)
- esc [ C - forward_char (right arrow)
- esc [ D - backward_char (left arrow)
- X.EE
- X
- X.SH INITIALIZATION FILE
- X.PP
- X
- The \fIile\fP initialization file has two parts. The first part is
- also the first line of the file. This line contains the delimiter
- characters that will be used by the \fBforward_word\fP,
- X\fBbackward_word\fP, and \fBdelete_word\fP actions. Each character on
- the line becomes a delimiter character.
- X
- The second part of the file is a list of table numbers, characters,
- and actions or strings. \fIile\fP has 4 action tables. Each action
- table contains an action or string for each possible character.
- X\fIile\fP decides what to do with a character by looking it up in the
- table and executing the action associated with the character or by
- passing the string one character at a time into \fIile\fP as if it had
- been typed by the user. Normally only table 0 is used. But, the
- X\fBescape\fP actions cause the next character to be looked up in a
- different table. The \fBescape\fP actions make it possible to map
- multiple character sequences to actions.
- X
- By default, all entries in table 0 are bound to the \fBinsert\fP
- action, and all entries in the other tables are bound to the
- X\fBbell\fP action. User specified bindings override these defaults.
- The following example is an initialization file that sets up the same
- key and delimiter bindings as the \fIile\fP default bindings.
- X
- X.PP
- X.SH Example \fI.ilerc\fP file
- X.EX 0
- X /.-
- X
- X0^A=start_of_line
- X0^B=backward_char
- X0^E=end_of_line
- X0^F=forward_char
- X0^K=erase_to_end_of_line
- X0^L=retype_line
- X0^N=forward_history
- X0^P=backward_history
- X0^R=search_backward_history
- X0^V=quote
- X0^T=transpose_chars
- X0\\177=delete_char
- X0^[=escape_1
- X0^M=add_to_history
- X0^J=add_to_history
- X0^U=erase_line
- X0^X=delete_char_under
- X
- X0^C=pass
- X0^D=pass
- X0^Q=pass
- X0^S=pass
- X0^Z=pass
- X
- X1b=backward_word
- X1f=forward_word
- X1\\177=delete_word
- X1[=escape_2
- X1^[=complete_file
- X1s=complete_file_full
- X1p=query_path
- X1d=show_files
- X1u=upper_word
- X1l=lower_word
- X1c=capitalize_word
- X
- X2A=backward_history
- X2B=forward_history
- X2C=forward_char
- X2D=backward_char
- X.EE
- X
- X.PP
- X
- The first character on each key binding line is the index of the table
- to place the key binding in. Valid values for the index are 0, 1, 2,
- and 3.
- X
- X.PP
- X
- The second character on the line is either the character to bind or an
- indicator that tells how to find out what character to bind. If the
- second character is any character besides '^' or '\\' then the action
- is bound to that character.
- X
- X.PP
- X
- If the second character on the line is '^' then the next character is
- taken as the name of a control character. So ^H is backspace and ^[ is
- escape.
- X
- X.PP
- X
- If the second character on the line is a '\\' and the next character
- is a digit between 0 and 7 the the following characters are
- interpreted as an octal number that indicates which character to bind
- the action to. If the character immediately after the '\\' is not an
- octal digit then the action is bound to that character. For example,
- to get the '^' character you would use '\\^'.
- X
- X.PP
- X
- The next character on the line is always '='. Following the equal sign
- is the name of an action or a string. The actions are defined in the
- following table.
- X
- X.PP
- X.SH Actions
- X
- X.IP "\fBbell\fP" 20
- X
- Send a bell (^G) character to the terminal. Hopefully the bell will
- ring. This action is a nice way to tell the user that an invalid
- sequence of keys has been typed.
- X
- X.IP "\fBpass\fP" 20
- X
- Pass the character to the program running under \fIile\fP. Do not echo
- the character, do not insert it into the edit buffer. Just pass it
- along. This is useful for characters like ^C, ^Z, ^Q, and ^S that
- have special meaning and shouldn't be held up in the edit buffer
- waiting to be sent.
- X
- X.IP "\fBinsert\fP" 20
- X
- Insert the character into the edit buffer. If there are already 256
- characters in the buffer \fIile\fP will beep and refuse to put the
- character in the buffer.
- X
- X.IP "\fBtranspose_chars\fP" 20
- X
- Swap the character under the cursor with the character to the left of
- the cursor and move the cursor one character to the right. This is
- handy for correcting letter transposition errors.
- X
- X.IP "\fBdelete_char\fP" 20
- X
- X Delete the character directly to the left of the cursor from the edit
- buffer.
- X
- X.IP "\fBdelete_char_under\fP" 20
- X
- Delete the character under the cursor from the edit buffer.
- X
- X.IP "\fBquote\fP" 20
- X
- The next character to come into \fIile\fP will be inserted into the edit
- buffer. This allows you to put characters into the edit buffer
- that are bound to an action other than insert.
- X
- X.IP "\fBescape_1\fP" 20
- X
- Look up the next character in action table 1 instead of action table 0.
- X
- X.IP "\fBescape_2\fP" 20
- X
- Look up the next character in action table 2 instead of action table 0.
- X
- X.IP "\fBescape_3\fP" 20
- X
- Look up the next character in action table 3 instead of action table 0.
- X
- X.IP "\fBdelete_word\fP" 20
- X
- Delete the word directly to the left of the cursor. A word is a sequence
- of characters surrounded by delimiter characters.
- X
- X.IP "\fBforward_word\fP" 20
- X
- Move the cursor to the right past the next word. A word is a sequence of
- characters surrounded by delimiter characters.
- X
- X.IP "\fBbackward_word\fP" 20
- X
- Move the cursor to the left past the next word. A word is a sequence of
- characters surrounded by delimiter characters.
- X
- X.IP "\fBupper_word\fP" 20
- X
- Starting with the character under the cursor, convert the word to the
- right of the cursor to upper case.
- X
- X.IP "\fBlower_word\fP" 20
- X
- Starting with the character under the cursor, convert the word to the
- right of the cursor to lower case.
- X
- X.IP "\fBcapitalize_word\fP" 20
- X
- Convert the character under the cursor to upper case. Convert the word to
- the right of the cursor to lower case.
- X
- X.IP "\fBstart_of_line\fP" 20
- X
- Move the cursor to the left most character in the edit buffer.
- X
- X.IP "\fBbackward_char\fP" 20
- X
- Move the cursor to the left one character.
- X
- X.IP "\fBend_of_line\fP" 20
- X
- Move the cursor past the last character in the edit buffer.
- X
- X.IP "\fBforward_char\fP" 20
- X
- Move the cursor to the right one character.
- X
- X.IP "\fBadd_to_history\fP" 20
- X
- Add the contents of the edit buffer to the history buffer and pass the line
- along to the program running under \fIile\fP.
- X
- X.IP "\fBerase_line\fP" 20
- X
- Clear the line. Erase all characters on the line.
- X
- X.IP "\fBerase_to_end_of_line\fP" 20
- X
- Delete the character under the cursor and all character to the left
- of the cursor from the edit buffer.
- X
- X.IP "\fBretype_line\fP" 20
- X
- Retype the contents of the current edit buffer. This is handy when system
- messages or other asynchronous output has garbled the input line.
- X
- X.IP "\fBforward_history\fP" 20
- X
- Display the next entry in the history buffer. If you are already at the
- most recent entry display a blank line. If you try to go forward past
- the blank line this command will beep at you.
- X
- X.IP "\fBbackward_history\fP" 20
- X
- Display the previous entry in the history buffer. If there are no older
- entries in the buffer, beep.
- X
- X.IP "\fBsearch_backward_history\fP" 20
- X
- Search for a line in the history buffer that starts with the
- characters to the left of the cursor. If a match is found the matched
- line is displayed. If no match is found this command will beep at you.
- X
- X.IP "\fBcomplete_file\fP" 20
- X
- Take the word currently under, or immediately to the left of the
- cursor and treat it as a partial file name and path name. If there is
- only one file in the directory that starts with the partial file name
- then fill in the rest of the file name in the input line. If more than
- one file starts with the partial file name fill in the longest common
- starting string of those file names. If the path is specified as "~/"
- then look in the directory named by $HOME.
- X
- If the path is specified as "~name", where name is a user login name or
- a partial user login name, then look in the users login directory. If
- more than one match is found for a partial user name then \fIile\fP
- will beep. When completing a file name, a partial user name will be
- completed at the same time the file name is being completed.
- X
- If you use abbreviated path names like "./file", "../file",
- X"dir/file", or "file" \fIile\fP uses the path name saved by the most
- recent \fBquery_path\fP command or the value of $PWD at the time
- X\fIile\fp was started.
- X
- X.IP "\fBcomplete_file_full\fP" 20
- X
- Like \fBcomplete_file\fP but abbreviations like "~/" are replaced by
- the full path that they stand for. This is handy when you want to use
- abbreviated path names but the program you are talking to doesn't
- understand the abbreviations.
- X
- Read the discussion of file name completion under \fBcomplete_file\fP
- for more information.
- X
- X.IP "\fBquery_path\fP" 20
- X
- X\fIile\fP isn't the shell and doesn't know what the current working
- directory is. But, \fIile\fP tries to do file name completion as if it
- did. To do this task \fIile\fP keeps around the path to the current
- working directory. When \fIile\fP is started up this path is
- initialized from $PWD. The \fBquery_path\fP command is provided to
- allow users to update this path at any time.
- X
- When \fBquery_path\fP is invoked \fIile\fP makes the blatant
- assumption that the program running under \fIile\fP is a shell and
- sends the shell command "pwd" to that program. Whatever comes back
- from the program is assumed to the path to the current working
- directory. The next response from the program is assumed to be a new
- prompt from the shell and is ignored.
- X
- X.IP "\fBshow_files\fP" 20
- X
- Take the word currently under, or immediately to the left of, the cursor and
- treat it as a partial file name and path name. List all the files that start
- with the partial file name in the directory specified by the path name.
- X
- Read the discussion of file name completion under \fBcomplete_file\fP
- for more information.
- X
- X.PP
- X.SH Strings
- X
- In addition to being able to bind a character sequence to an action \fIile\fP
- allows characters sequences to be bound to strings of characters. When a string
- is invoked the characters in the string are treated as if they were typed
- by the user. For example, if the line:
- X.EX 0
- X0^G=ring^Ma^Mbell^M
- X.EE
- was in your \fI.ilerc\fP file, typing control G would cause three
- lines to be typed as if the user typed them. Using the default bindings,
- unless there is a ^J or ^M in the string the string will be inserted
- in the current line but not sent along until the the user actually
- presses return.
- X
- X.PP
- X.SH Error Messages
- X.PP
- X
- When \fIile\fP encounters errors it prints a message and terminates.
- X\fIile\fP can print several standard error message. It can also print
- a few messages that are specific to \fIile\fP.
- X
- X.IP "\fBile: unable to allocate pty/tty pair\fP" 20
- X
- There are no free pty devices in the system. You can either try again later,
- and hope someone has freed a pty for you to use, or you can grab your
- system manager and try to get more pty devices configured.
- X
- X.IP "\fBile: '=' missing on line #\fP" 20
- X
- In a character binding line you left out the '=' character. Or, you did
- something that confused the initialization file reader into thinking there
- should be an '=' where you didn't think there should be one.
- X
- X.IP "\fBile: error in initialization file on line #\fP" 20
- X
- This means that the first character of a character binding line wasn't
- a newline or a '0', '1', '2', or '3'. It could also mean that the
- initialization file reader is confused.
- X
- X.IP "\fBile: can't find terminal\fP" 20
- X
- X\fIile\fP could not find a termcap entry for the terminal named by the TERM
- environment variable. Since it can't find it \fIile\fP can't figure out
- how to use it.
- X
- X.IP "\fBile: can't run on terminal\fP" 20
- X
- The terminal named in your TERM environment variable doesn't support
- the capabilities \fIile\fP needs to run. So \fIile\fP doesn't even try.
- X
- X.PP
- X.SH BUGS
- X.PP
- X\fIile\fP changes the input mode on the controlling terminal to RAW. This
- confuses xterm. It is a good idea to include the line:
- X.EX 0
- stty cooked -nl echo tabs crt decctlq -litout
- X.EE
- in your .cshrc file when using xterm. Otherwise your new xterm windows
- come up in an unusable state.
- X.PP
- X\fIile\fP requires a terminal that supports the termcap le, ce, bl,
- nl, and cr capabilities. If your terminal doesn't provide these,
- X\fIile\fP will refuse to run on your terminal.
- X.PP
- A misspelled action name in an \fIilerc\fP will be treated as a string.
- This means that typing the sequence of characters that should
- invoke the action will actually cause the misspelled name to be inserted
- in the input line.
- X.PP
- X.SH FILES
- X.PP
- X $HOME/.ilerc
- X ./.ilerc
- X.PP
- X.SH SEE ALSO
- X.PP
- stty(1), xterm(1), csh(1), termcap(5)
- X
- X.SH COPYRIGHT
- X.ce 4
- COPYRIGHT 1988
- XEvans & Sutherland Computer Corporation
- Salt Lake City, Utah
- All Rights Reserved.
- X.LP
- THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
- SHOULD NOT BE CONSTRUED AS A COMMITMENT BY EVANS & SUTHERLAND.
- XEVANS & SUTHERLAND MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY
- OF THIS SOFTWARE FOR
- ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
- X.LP
- IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS,
- APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT
- SET FORTH ABOVE.
- X.LP
- Permission to use, copy, modify, and distribute this software and its
- documentation for any purpose and without fee is hereby granted, provided
- that the above copyright notice appear in all copies and that both the
- copyright notice and this permission notice appear in supporting documentation,
- and that the name of Evans & Sutherland not be used in advertising
- or publicity pertaining to distribution of the software without specific,
- written prior permission.
- X.SH AUTHOR
- Robert C. Pendleton <bobp@hal.com>
- X.LP
- X
- END_OF_FILE
- if test 16441 -ne `wc -c <'ile.1'`; then
- echo shar: \"'ile.1'\" unpacked with wrong size!
- fi
- # end of 'ile.1'
- fi
- if test -f 'ile.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'ile.c'\"
- else
- echo shar: Extracting \"'ile.c'\" \(60367 characters\)
- sed "s/^X//" >'ile.c' <<'END_OF_FILE'
- X/*
- X COPYRIGHT 1988
- X Evans & Sutherland Computer Corporation
- X Salt Lake City, Utah
- X All Rights Reserved.
- X
- X THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE
- X WITHOUT NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT
- X BY EVANS & SUTHERLAND. EVANS & SUTHERLAND MAKES NO
- X REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
- X ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR
- X IMPLIED WARRANTY.
- X
- X IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE
- X COPYRIGHT RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE
- X DERIVATIVE WORK IN ADDITION TO THAT SET FORTH ABOVE.
- X
- X Permission to use, copy, modify, and distribute this
- X software and its documentation for any purpose and without
- X fee is hereby granted, provided that the above copyright
- X notice appear in all copies and that both the copyright
- X notice and this permission notice appear in supporting docu-
- X mentation, and that the name of Evans & Sutherland not be
- X used in advertising or publicity pertaining to distribution
- X of the software without specific, written prior permission.
- X
- Written by:
- X
- X Robert C. Pendleton <bobp@hal.com>
- X
- Grateful acknowledgement is made of code and ideas contributed by
- X
- X Ian Donaldson,
- X Department of Communications & Electronic Engineering,
- X Royal Melbourne Institute of Technology,
- X Melbourne, Australia.
- X
- X$Header: ile.c,v 2.3 88/11/11 10:31:56 bpendlet Exp $
- X*/
- X
- X/*
- ile is compiled using:
- X
- cc ile.c -o ile -ltermcap
- X*/
- X
- X#include <stdio.h>
- X#include <fcntl.h>
- X#include <sgtty.h>
- X#include <signal.h>
- X#include <string.h>
- X#include <strings.h>
- X#include <pwd.h>
- X#include <utmp.h>
- X#include <errno.h>
- X#include <sys/ioctl.h>
- X#include <sys/types.h>
- X#include <sys/dir.h>
- X#include <sys/file.h>
- X#include <sys/time.h>
- X
- X/*------------------------------------------------------------------*/
- X/*
- Definitions of system stuff.
- X*/
- X
- extern int errno;
- X
- long lseek();
- char *malloc();
- char *realloc();
- time_t time();
- X
- X/*------------------------------------------------------------------*/
- X
- X#define FALSE 0
- X#define TRUE 1
- X
- X#define READ 0
- X#define WRITE 1
- X#define ERROR 2
- X
- X#define BUFFER_SIZE 255
- X
- X#define HISTORY_SIZE 101
- X
- X#define USER_NAME_SIZE 8
- X
- X#define EOL (-2)
- X/*------------------------------------------------------------------*/
- X
- X/* special characters used by ile */
- X
- X#define del '\177'
- X
- X#define CA '\1'
- X#define CB '\2'
- X#define CC '\3'
- X#define CD '\4'
- X#define CE '\5'
- X#define CF '\6'
- X#define bel '\7'
- X#define bs '\10'
- X#define CI '\11'
- X#define nl '\12'
- X#define CK '\13'
- X#define CL '\14'
- X#define cr '\15'
- X#define CN '\16'
- X#define CO '\17'
- X#define CP '\20'
- X#define CQ '\21'
- X#define CR '\22'
- X#define CS '\23'
- X#define CT '\24'
- X#define CU '\25'
- X#define CV '\26'
- X#define CW '\27'
- X#define CX '\30'
- X#define CY '\31'
- X#define CZ '\32'
- X
- X#define esc '\33'
- X
- X/*------------------------------------------------------------------*/
- X/* areas and varaibles used to get termcap information */
- X
- char *getenv();
- X
- char *tgetnum();
- char *tgetflag();
- char *tgetstr();
- X
- char termcap_entry[1024]; /* termcap entry for the users terminal */
- char term_seqs[1024]; /* area to store control sequences in */
- char *where = term_seqs;
- X
- char *cle; /* move cursor left one space */
- X
- char *cce; /* clear to end of line */
- X
- char *cbl; /* audible bell */
- X
- char *cnl; /* new line character */
- X
- char *ccr; /* carriage return */
- X
- X/*------------------------------------------------------------------*/
- X/*
- The value of HOME
- X*/
- X
- char *homedir = NULL;
- X
- X/*------------------------------------------------------------------*/
- X/*
- The current working directory as set by query_path.
- initialized to PWD
- X*/
- X
- char currentdir[MAXNAMLEN + 1] = "";
- X
- X/*------------------------------------------------------------------*/
- X
- X/* tty status flags */
- X/*
- X The original tty status flags are stored so that they can be
- X restored when ile exits.
- X*/
- X
- struct sgttyb tty_sgttyb;
- struct tchars tty_tchars;
- struct ltchars tty_ltchars;
- struct winsize tty_winsize;
- int windowchanged;
- int tty_ldisc;
- int tty_mode;
- X
- X/*------------------------------------------------------------------*/
- X
- X/* file descriptors for tty and pty */
- X
- int master_pty;
- int slave_tty;
- X
- X/* the names of the tty and pty opened by getpty */
- X
- char ttydev[] = "/dev/ttyxx";
- char ptydev[] = "/dev/ptyxx";
- X
- X/* path and name of the lock file */
- X
- char lock[] = "/tmp/ile.lock";
- X
- X/*------------------------------------------------------------------*/
- X/*
- X getpty opens a pty, storing file descriptors in pty and tty.
- X It trys pairs in order until it finds a pair that is not in use.
- X*/
- X
- getpty(pty, tty)
- X int *pty;
- X int *tty;
- X
- X{
- X int devindex;
- X int letter;
- X
- X static char ptychar1[] = "pqrstuvwxyz";
- X static char ptychar2[] = "0123456789abcdef";
- X
- X letter = 0;
- X while (letter < 11)
- X {
- X ttydev[strlen(ttydev) - 2] = ptychar1[letter];
- X ptydev[strlen(ptydev) - 2] = ptychar1[letter];
- X letter++;
- X
- X devindex = 0;
- X while (devindex < 16)
- X {
- X ttydev[strlen(ttydev) - 1] = ptychar2[devindex];
- X ptydev[strlen(ptydev) - 1] = ptychar2[devindex];
- X devindex++;
- X
- X if ((*pty = open(ptydev, O_RDWR)) >= 0)
- X {
- X if ((*tty = open(ttydev, O_RDWR)) >= 0)
- X {
- X return;
- X }
- X else
- X {
- X (void) close(*pty);
- X }
- X }
- X }
- X }
- X
- X (void) fprintf(stderr, "ile: unable to allocate pty/tty pair\n");
- X exit(1);
- X /* NOTREACHED */
- X}
- X
- X/*------------------------------------------------------------------*/
- X/*
- Termcap entries may have a sequences of digits optionally followed
- by a '*' in front of the actual sequence. This routine increments
- the pointer past this information.
- X*/
- void
- strip(ptr)
- X char **ptr;
- X{
- X while (('0' <= **ptr) && (**ptr <= '9'))
- X {
- X (*ptr)++;
- X }
- X
- X if (**ptr == '*')
- X {
- X (*ptr)++;
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Set up everything needed to use the control sequences from the
- termcap entry for the terminal.
- X*/
- void
- get_termcap()
- X{
- X char *terminal_type; /* type of terminal */
- X
- X /* get the terminal name */
- X
- X terminal_type = getenv("TERM");
- X
- X /* get termcap entry */
- X
- X if (tgetent(termcap_entry, terminal_type) < 1)
- X {
- X (void) fprintf(stderr, "ile: can't find %s\n", terminal_type);
- X exit(1);
- X /* NOTREACHED */
- X }
- X
- X /* get the control sequences ile needs */
- X
- X if ((cbl = tgetstr("bl", &where)) == NULL)
- X {
- X cbl = "\7";
- X }
- X
- X if ((cnl = tgetstr("nl", &where)) == NULL)
- X {
- X cnl = "\n";
- X }
- X
- X if ((ccr = tgetstr("cr", &where)) == NULL)
- X {
- X ccr = "\r";
- X }
- X
- X if ((cle = tgetstr("le", &where)) == NULL)
- X {
- X if (tgetflag("bs"))
- X {
- X cle = "\b";
- X }
- X }
- X
- X if ((cle == NULL) ||
- X ((cce = tgetstr("ce", &where)) == NULL))
- X {
- X (void) fprintf(stderr,
- X "ile: can't run on %s (need capabilities \"le\" and \"ce\")\n",
- X terminal_type);
- X exit(1);
- X /* NOTREACHED */
- X }
- X
- X /* strip timing info from strings */
- X
- X strip(&cle);
- X strip(&cce);
- X strip(&cbl);
- X strip(&cnl);
- X strip(&ccr);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- If the window changes size, tell the slave_tty about it.
- X*/
- void
- change_window()
- X{
- X int pgrp;
- X
- X (void) ioctl(READ, TIOCGWINSZ, &tty_winsize);
- X (void) ioctl(slave_tty, TIOCSWINSZ, &tty_winsize);
- X
- X (void) ioctl(slave_tty, TIOCGPGRP, (char *) &pgrp);
- X (void) killpg(pgrp, SIGWINCH);
- X
- X /* note the change so that we don't die after select */
- X
- X windowchanged = TRUE;
- X}
- X/*------------------------------------------------------------------*/
- X/*
- X * set/clear the utmp slot for the pty
- X */
- int
- setutmp(fd, set)
- X{
- X int old0;
- X int old1;
- X int old2;
- X int slot;
- X int f;
- X struct utmp ut;
- X
- X /* Must make fd's 0,1,2 correspond to slave_tty for ttyslot() to
- X * function. Ugh! Why doesn't ttyslot() accept a fd argument?
- X *
- X * save fd's */
- X
- X old0 = dup(0);
- X old1 = dup(1);
- X old2 = dup(2);
- X
- X if (old0 == -1 || old1 == -1 || old2 == -1)
- X {
- X perror("ile: dup");
- X return (-1); /* file table full ? */
- X }
- X
- X /* set fd's 0,1,2 for ttyslot() */
- X
- X (void) dup2(fd, 0);
- X (void) dup2(fd, 1);
- X (void) dup2(fd, 2);
- X
- X slot = ttyslot();
- X
- X /* put the fd's back */
- X
- X (void) dup2(old0, 0);
- X (void) dup2(old1, 1);
- X (void) dup2(old2, 2);
- X
- X (void) close(old0);
- X (void) close(old1);
- X (void) close(old2);
- X
- X if (slot < 0)
- X {
- X (void) fprintf(stderr, "ile: don't know where you are\n");
- X return (-1);
- X }
- X
- X f = open("/etc/utmp", O_WRONLY);
- X if (f == -1)
- X {
- X return (-1);
- X }
- X
- X bzero((char *) &ut, sizeof(ut));
- X
- X if (set)
- X {
- X struct passwd *pw;
- X char *cp;
- X
- X pw = getpwuid(getuid());
- X if (pw == 0)
- X {
- X (void) fprintf(stderr, "ile: who are you?\n");
- X (void) close(f);
- X return (-1);
- X }
- X
- X /* skip "/dev/" */
- X
- X cp = rindex(ttydev, '/');
- X if (cp == 0)
- X {
- X cp = ttydev;
- X }
- X else
- X {
- X cp++;
- X }
- X
- X (void) strncpy(ut.ut_line, cp, sizeof(ut.ut_line));
- X (void) strncpy(ut.ut_name, pw->pw_name, sizeof(ut.ut_line));
- X (void) time(&ut.ut_time);
- X }
- X (void) lseek(f, (long) (slot * sizeof(struct utmp)), L_SET);
- X (void) write(f, (char *) &ut, sizeof(ut));
- X (void) close(f);
- X
- X return (0);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- clean up and leave.
- X
- This function is bound to the SIGCHLD signal so that when the
- child process exits, so does ile. It is also called when an exception
- is detected by select() in ile().
- X*/
- void
- clean_up()
- X{
- X int pgrp;
- X
- X /* kill off the child process */
- X
- X (void) ioctl(slave_tty, TIOCGPGRP, (char *) &pgrp);
- X (void) killpg(pgrp, SIGTERM);
- X
- X /* restore terminal status */
- X
- X (void) ioctl(READ, TIOCSETP, &tty_sgttyb);
- X (void) ioctl(READ, TIOCSETC, &tty_tchars);
- X (void) ioctl(READ, TIOCSLTC, &tty_ltchars);
- X (void) ioctl(READ, TIOCLSET, &tty_mode);
- X
- X /* "logout" the user */
- X
- X (void) setutmp(slave_tty, FALSE);
- X
- X /* clean up the tty/pty pair */
- X
- X (void) close(master_pty);
- X (void) close(slave_tty);
- X
- X /* make things look nice */
- X
- X fputs(cnl, stdout);
- X
- X exit(0);
- X /* NOTREACHED */
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Write a line to the slave_tty.
- Get the slave_tty parameters, turn off echo, send the line to the
- slave_tty, restore the slave_tty paramters to the way they were
- before. If echo was already off, this will have no effect.
- X*/
- write_line(line, length)
- X char *line;
- X int length;
- X{
- X struct sgttyb params;
- X struct sgttyb new_params;
- X
- X /* get the current parameters */
- X
- X (void) ioctl(slave_tty, TIOCGETP, ¶ms);
- X new_params = params;
- X new_params.sg_flags &= ~ECHO;
- X
- X /* turn off echo so we don't see the characters twice */
- X
- X (void) ioctl(slave_tty, TIOCSETP, &new_params);
- X
- X (void) write(master_pty, line, length);
- X
- X /* set the parameters back the way they were */
- X
- X (void) ioctl(slave_tty, TIOCSETN, ¶ms);
- X}
- X
- X/*------------------------------------------------------------------*/
- X/*
- The editing routines are called through the edit variable. This allows
- the quote and escape commands to be implemented as a straight forward
- state machine instead of requiring state flags and complex switch
- statements.
- X*/
- X/*------------------------------------------------------------------*/
- X
- X/* line edit buffer */
- X
- static char line[BUFFER_SIZE];
- X
- static int point; /* insertion point */
- static int length; /* total chars in buffer */
- X
- X/* procedure to edit next character */
- X
- void (*edit) ();
- X
- X/* history buffer */
- X
- struct
- X{
- X int length;
- X char *line;
- X} hist[HISTORY_SIZE];
- X
- int head; /* insertion point */
- int here; /* current displayed line */
- X
- X/*------------------------------------------------------------------*/
- X/*
- The delimiter vector is used by the forward, backward, and delete
- word operations to decide that a character is a delimiter.
- X*/
- X/*------------------------------------------------------------------*/
- X
- X#define CHAR_SET_SIZE 127
- X#define CHAR_MASK 0177
- X
- char delimit[CHAR_SET_SIZE];
- X
- X/*------------------------------------------------------------------*/
- X/*
- The action_table is used to bind sequences of keys to operations or strings.
- X*/
- X/*------------------------------------------------------------------*/
- X
- typedef enum
- X{
- X is_action, is_string
- X} action_type;
- X
- struct
- X{
- X action_type flag;
- X union
- X {
- X void (*action) ();
- X char *string;
- X } aors;
- X} action_table[4][CHAR_SET_SIZE];
- X
- X/*------------------------------------------------------------------*/
- X
- void echo();
- void echoline();
- void cleartoend();
- void clearline();
- void backspace();
- void quote_edit();
- void edit_0();
- void edit_1();
- void edit_2();
- void edit_3();
- void bell();
- void insert();
- X
- X/*------------------------------------------------------------------*/
- X/*
- The following routines are action routines that are executed by the
- editor to carry out commands. Each routine has a single character
- argument. Each routine is invoked with the character that caused it
- to be invoked as its argument.
- X
- The argument isn't always useful, but it is included to provide a
- consistent interface for the routines.
- X*/
- X/*------------------------------------------------------------------*/
- X/*
- Given a specific directory and the starting string of a file name,
- find the longest partial file name that starts with the substring.
- X*/
- void
- complete_file_name(dir, name)
- X char *dir;
- X char *name;
- X{
- X DIR *dirp;
- X struct direct *dp;
- X
- X int len;
- X int maxlen;
- X int oldlen;
- X
- X char oldname[MAXNAMLEN + 1];
- X char newname[MAXNAMLEN + 1];
- X
- X if ((dir != NULL) &&
- X (name != NULL) &&
- X ((oldlen = strlen(name)) > 0) &&
- X ((dirp = opendir(dir)) != NULL))
- X {
- X maxlen = oldlen;
- X (void) strcpy(oldname, name);
- X (void) strcpy(newname, name);
- X
- X /* find the longest name starting with name */
- X
- X for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
- X {
- X if (dp->d_name != NULL)
- X {
- X len = strlen(dp->d_name);
- X if ((maxlen < len) &&
- X (strncmp(oldname, dp->d_name, oldlen) == 0))
- X {
- X maxlen = len;
- X (void) strcpy(newname, dp->d_name);
- X }
- X }
- X }
- X
- X rewinddir(dirp);
- X
- X /* find the longest common sub string */
- X
- X for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
- X {
- X if (dp->d_name != NULL)
- X {
- X len = strlen(dp->d_name);
- X if ((len <= maxlen) &&
- X (strncmp(oldname, dp->d_name, oldlen) == 0))
- X {
- X for (;
- X (oldlen < len) &&
- X (strncmp(newname, dp->d_name, len) != 0);
- X len--);
- X
- X maxlen = len;
- X newname[maxlen] = '\0';
- X }
- X }
- X }
- X
- X if (strlen(name) != strlen(newname))
- X {
- X /* return the extended name */
- X
- X (void) strcpy(name, newname);
- X }
- X else
- X {
- X /* no difference so beep */
- X
- X bell('\0');
- X }
- X
- X (void) closedir(dirp);
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Hidden parameters to dirselect. They must be hidden because dirselect
- is passed as an argument to scandir.
- X*/
- X
- static char *namep;
- static int namelen;
- X
- X/*------------------------------------------------------------------*/
- X/*
- Passed to scandir. It is used to decide which files to display.
- X*/
- X
- static
- dirselect(dp)
- X struct direct *dp;
- X{
- X return (strncmp(dp->d_name, namep, namelen) == 0);
- X}
- X
- X/*------------------------------------------------------------------*/
- X/*
- List all the files in a given directory that start with the string
- passed as name.
- X*/
- void
- list_file_names(dir, name)
- X char *dir;
- X char *name;
- X{
- X struct direct **dlist;
- X int i;
- X int nfiles;
- X int colwidth;
- X int cols;
- X int ncols;
- X int nlines;
- X int alphasort();
- X
- X if (dir == NULL || name == NULL)
- X {
- X return;
- X }
- X
- X cols = tty_winsize.ws_col;
- X if (cols <= 0)
- X {
- X cols = 80;
- X }
- X
- X namelen = strlen(name);
- X namep = name;
- X
- X nfiles = scandir(dir, &dlist, dirselect, alphasort);
- X
- X /* determine the longest file name length */
- X
- X colwidth = 8; /* minimum width */
- X for (i = 0; i < nfiles; i++)
- X {
- X struct direct *dp;
- X
- X dp = dlist[i];
- X
- X if (dp->d_namlen > colwidth)
- X {
- X colwidth = dp->d_namlen;
- X }
- X }
- X
- X colwidth++; /* at least 1 space between them */
- X
- X /* print the names, sorted vertically per column */
- X ncols = cols / colwidth;
- X if (ncols == 0)
- X {
- X /* longest filename is wider than the screen */
- X ncols = 1;
- X }
- X
- X nlines = (nfiles + ncols - 1) / ncols;
- X
- X if (nfiles > 0)
- X {
- X for (i = 0; i < nlines; i++)
- X {
- X int j;
- X int l;
- X
- X l = 0;
- X for (j = 0; j < ncols; j++)
- X {
- X int m;
- X struct direct *dp;
- X
- X m = l + i;
- X if (m >= nfiles)
- X {
- X break;
- X }
- X
- X dp = dlist[m];
- X fputs(dp->d_name, stdout);
- X
- X if (j < (ncols - 1))
- X {
- X int k;
- X
- X for (k = dp->d_namlen; k < colwidth; k++)
- X {
- X (void) fputc(' ', stdout);
- X }
- X l += nlines;
- X }
- X }
- X fputs(ccr, stdout);
- X fputs(cnl, stdout);
- X }
- X }
- X free((char *) dlist);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Assuming that there is a file name under the cursor, return a path
- and a file name. If there is no path name return "."
- X*/
- int
- get_dir_and_name(dir, name, username, userend, start, middle, tail)
- X char *dir;
- X char *name;
- X char *username;
- X int *userend;
- X int *start;
- X int *middle;
- X int *tail;
- X{
- X int dirlen;
- X
- X int newstart;
- X
- X int punlen;
- X char pun[USER_NAME_SIZE + 1];
- X struct passwd *userpwd;
- X
- X int i;
- X
- X /* set the default path and file name */
- X
- X dir[0] = '\0';
- X name[0] = '\0';
- X username[0] = '\0';
- X
- X /* search for the start of the file name */
- X /* start will be left pointing to the first character of the path */
- X
- X for ((*start) = point;
- X ((0 < (*start)) && (line[(*start) - 1] != ' '));
- X (*start)--
- X );
- X
- X /* search for the end of the file name */
- X /* tail will be left pointing at the last character of the path */
- X
- X for ((*tail) = point - 1;
- X (((*tail) < (length - 1)) && (line[(*tail) + 1] != ' '));
- X (*tail)++
- X );
- X
- X /* search for the middle of the file name */
- X /* middle will be left pointing at the first character of the last
- X * element of the path */
- X
- X for ((*middle) = (*tail) + 1;
- X ((0 < (*middle)) &&
- X (line[(*middle) - 1] != '/') &&
- X (line[(*middle) - 1] != ' '));
- X (*middle)--
- X );
- X
- X /* copy path from line to dir */
- X
- X /* what base path */
- X
- X newstart = (*start);
- X
- X if ((line[newstart] == '~') &&
- X ((newstart + 1) < length) &&
- X (line[newstart + 1] == '/'))
- X {
- X /* "~/" means use the value of HOME */
- X
- X newstart++;
- X (void) strcpy(dir, homedir);
- X }
- X else if (line[newstart] == '~')
- X {
- X /* "~username" means use the users login directory */
- X
- X /* search for the end of the user name */
- X
- X for ((*userend) = newstart,
- X punlen = 0;
- X (((*userend) < (length - 1)) &&
- X (line[(*userend) + 1] != ' ') &&
- X (line[(*userend) + 1] != '/'));
- X (*userend)++,
- X punlen++);
- X
- X /* make middle point to middle */
- X
- X if ((*start) == (*middle))
- X {
- X (*middle) = (*start) + punlen + 1;
- X }
- X
- X /* extract partial user name from line */
- X
- X (void) strncpy(pun, &line[newstart + 1], punlen);
- X pun[punlen] = '\0';
- X
- X /* search passwd file for partial match */
- X
- X for (userpwd = getpwent();
- X userpwd != NULL;
- X userpwd = getpwent())
- X {
- X if ((punlen <= strlen(userpwd->pw_name)) &&
- X (strncmp(pun, userpwd->pw_name, punlen) == 0))
- X {
- X
- X /* we have a partial match, record it */
- X
- X if (strlen(dir) == 0)
- X {
- X newstart = (*userend) + 1;
- X (void) strcpy(dir, userpwd->pw_dir);
- X (void) strcpy(username, userpwd->pw_name);
- X }
- X else
- X {
- X /* second partial match, forget the first one. */
- X
- X newstart = (*start);
- X dir[0] = '\0';
- X username[0] = '\0';
- X return (FALSE);
- X }
- X
- X }
- X }
- X (void) setpwent();
- X }
- X else if ((line[newstart] == '.') &&
- X ((newstart + 1) < length) &&
- X (line[newstart + 1] == '/'))
- X {
- X /* if it's "./" use current dir */
- X
- X newstart++;
- X (void) strcpy(dir, currentdir);
- X }
- X else if ((line[newstart] == '.') &&
- X ((newstart + 1) < length) &&
- X (line[newstart + 1] == '.') &&
- X ((newstart + 2) < length) &&
- X (line[newstart + 2] == '/'))
- X {
- X /* if it's "../" strip off one name from currentdir and use that */
- X
- X newstart += 2;
- X (void) strcpy(dir, currentdir);
- X for (i = strlen(dir); (i > 0) && (dir[i] != '/'); i--)
- X {
- X /* nothing */
- X }
- X dir[i] = '\0';
- X }
- X else if (line[newstart] != '/')
- X {
- X /* doesn't start with a "/"? use currentdir */
- X
- X (void) strcpy(dir, currentdir);
- X (void) strcat(dir, "/");
- X }
- X
- X /* add on the rest of the path */
- X
- X dirlen = strlen(dir);
- X for (i = 0; i < ((*middle) - newstart); i++)
- X {
- X dir[dirlen + i] = line[newstart + i];
- X }
- X dir[dirlen + i] = '\0';
- X
- X /* copy file name from line to name */
- X
- X for (i = 0; i < ((*tail) - (*middle) + 1); i++)
- X {
- X name[i] = line[(*middle) + i];
- X }
- X name[i] = '\0';
- X
- X return (TRUE);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Perform file name completion. Put the full path and file name in the
- line.
- X*/
- X/*ARGSUSED*/
- void
- complete_file_full(ch)
- X char ch;
- X{
- X char dir[10 * (MAXNAMLEN + 1)];
- X char name[MAXNAMLEN + 1];
- X char username[USER_NAME_SIZE + 1];
- X
- X char newline[BUFFER_SIZE];
- X int newlength;
- X int newpoint;
- X
- X int userend;
- X int start;
- X int middle;
- X int tail;
- X
- X int i;
- X
- X /* get the path and file name in the line */
- X
- X if (get_dir_and_name(dir,
- X name,
- X username,
- X &userend,
- X &start,
- X &middle,
- X &tail))
- X {
- X
- X /* complete the file name if possible */
- X
- X complete_file_name(dir, name);
- X
- X /* create a new line */
- X
- X /* start with the line prefix */
- X
- X (void) strncpy(newline, line, start);
- X newline[start] = '\0';
- X
- X /* add in the new path */
- X
- X (void) strcat(newline, dir);
- X
- X /* stick in the new file name */
- X
- X (void) strcat(newline, name);
- X newpoint = strlen(newline);
- X
- X /* finish with the line postfix */
- X
- X (void) strncat(newline, &line[tail + 1], (length - tail - 1));
- X newlength = strlen(newline);
- X
- X /* display the new line */
- X
- X clearline('\0');
- X
- X point = newpoint;
- X length = newlength;
- X (void) strncpy(line, newline, newlength);
- X
- X echoline(line, length);
- X
- X for (i = point; i < length; i++)
- X {
- X backspace(line[i]);
- X }
- X }
- X else
- X {
- X bell('\0');
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Perform file name completion in much the same style as csh.
- X*/
- X/*ARGSUSED*/
- void
- complete_file(ch)
- X char ch;
- X{
- X char dir[10 * (MAXNAMLEN + 1)];
- X char name[MAXNAMLEN + 1];
- X char username[USER_NAME_SIZE + 1];
- X
- X int userend;
- X int start;
- X int middle;
- X int tail;
- X
- X char newline[BUFFER_SIZE];
- X int newlength;
- X int newpoint;
- X
- X int userlen;
- X int len;
- X
- X int i;
- X
- X /* get the path and file name in the line */
- X
- X if (get_dir_and_name(dir,
- X name,
- X username,
- X &userend,
- X &start,
- X &middle,
- X &tail))
- X {
- X /* how long is the user name */
- X
- X userlen = strlen(username);
- X
- X /* complete the file name if possible */
- X
- X complete_file_name(dir, name);
- X /* create a new line */
- X
- X /* start with the line prefix */
- X
- X (void) strncpy(newline, line, start);
- X newline[start] = '\0';
- X
- X /* add in the new username */
- X
- X if (userlen != 0)
- X {
- X /* put in new user name */
- X
- X (void) strcat(newline, "~");
- X (void) strcat(newline, username);
- X len = strlen(newline);
- X
- X /* put in the existing path */
- X
- X (void) strncat(newline, &line[userend + 1], middle - userend - 1);
- X newline[len + (middle - userend - 1)] = '\0';
- X }
- X else
- X {
- X /* put in the existing path */
- X
- X len = strlen(newline);
- X (void) strncat(newline, &line[start], middle - start);
- X newline[len + (middle - start)] = '\0';
- X }
- X
- X /* stick in the new file name */
- X
- X (void) strcat(newline, name);
- X newpoint = strlen(newline);
- X
- X /* finish with the line postfix */
- X
- X (void) strncat(newline, &line[tail + 1], (length - tail - 1));
- X newlength = strlen(newline);
- X
- X /* display the new line */
- X
- X clearline('\0');
- X
- X point = newpoint;
- X length = newlength;
- X (void) strncpy(line, newline, newlength);
- X
- X echoline(line, length);
- X
- X for (i = point; i < length; i++)
- X {
- X backspace(line[i]);
- X }
- X }
- X else
- X {
- X bell('\0');
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- List the names of files that start with the directory path and
- file name under the cursor.
- X*/
- X/*ARGSUSED*/
- void
- show_files(ch)
- X char ch;
- X{
- X static char divider[] = "----------";
- X
- X void retype_line();
- X
- X char dir[10 * (MAXNAMLEN + 1)];
- X char name[MAXNAMLEN + 1];
- X char username[USER_NAME_SIZE + 1];
- X
- X int userend;
- X int start;
- X int middle;
- X int tail;
- X
- X if (get_dir_and_name(dir,
- X name,
- X username,
- X &userend,
- X &start,
- X &middle,
- X &tail))
- X {
- X
- X fputs(ccr, stdout);
- X fputs(cnl, stdout);
- X
- X fputs(divider, stdout);
- X
- X fputs(ccr, stdout);
- X fputs(cnl, stdout);
- X
- X list_file_names(dir, name);
- X
- X fputs(divider, stdout);
- X
- X fputs(ccr, stdout);
- X fputs(cnl, stdout);
- X
- X retype_line('\0');
- X }
- X else
- X {
- X bell('\0');
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Make the gross assumption that the program we are talking to is
- a shell and send "pwd\n" to it. Whatever comes back is saved as the
- value of currentdir.
- X*/
- X/*ARGSUSED*/
- void
- query_path(ch)
- X char ch;
- X{
- X static char command[] = "pwd\n";
- X char buffer[BUFFER_SIZE];
- X int readfd;
- X struct timeval timeout;
- X int status;
- X int cc;
- X int i;
- X
- X /* send the command to the shell, we hope. */
- X
- X write_line(command, strlen(command));
- X
- X /* read the directory path back */
- X
- X readfd = 1 << master_pty;
- X timeout.tv_sec = 2;
- X timeout.tv_usec = 0;
- X
- X do
- X {
- X status = select(32,
- X (fd_set *) & readfd,
- X (fd_set *) NULL,
- X (fd_set *) NULL,
- X &timeout);
- X
- X if (0 < status)
- X {
- X cc = read(master_pty, currentdir, sizeof(currentdir));
- X
- X /* strip off trailing control chars and blanks */
- X
- X for (i = cc - 1; (currentdir[i] <= ' ') && (i > 0); i--)
- X {
- X currentdir[i] = '\0';
- X }
- X
- X /* read the prompt so it can be ignored */
- X
- X readfd = 1 << master_pty;
- X timeout.tv_sec = 2;
- X timeout.tv_usec = 0;
- X
- X do
- X {
- X status = select(32,
- X (fd_set *) & readfd,
- X (fd_set *) NULL,
- X (fd_set *) NULL,
- X &timeout);
- X
- X if (0 < status)
- X {
- X cc = read(master_pty, buffer, sizeof(buffer));
- X }
- X else if ((-1 == status) && windowchanged)
- X {
- X windowchanged = FALSE;
- X }
- X } while (status == -1);
- X }
- X else if ((-1 == status) && windowchanged)
- X {
- X windowchanged = FALSE;
- X }
- X } while (status == -1);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Ring the bell on the terminal.
- X*/
- X/*ARGSUSED*/
- void
- bell(ch)
- X char ch;
- X{
- X fputs(cbl, stdout);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Pass characters to the slave. Don't mess with them at all.
- X*/
- void
- pass(ch)
- X char ch;
- X{
- X (void) write(master_pty, &ch, 1);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Insert a character at point in the line buffer. While we are at it
- update the display to show the insertion.
- X*/
- void
- insert(ch)
- X char ch;
- X{
- X int i;
- X
- X if (length < (BUFFER_SIZE - 2))
- X {
- X
- X /* display the character */
- X
- X echo(ch);
- X
- X /* redisplay the rest of the line */
- X
- X echoline(&line[point], (length - point));
- X
- X /* move the characters in the line buffer */
- X /* and put the cursor back at point */
- X
- X for (i = length; i > point; i--)
- X {
- X line[i] = line[i - 1];
- X backspace(line[i]);
- X }
- X
- X /* add the character to the line buffer */
- X /* and increment point and length */
- X
- X line[point] = ch;
- X length++;
- X point++;
- X }
- X else
- X {
- X bell('\0');
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Transpose the letter under the cursor and the letter immediately to
- the left of the cursor.
- X*/
- X/*ARGSUSED*/
- void
- transpose_chars(ch)
- X char ch;
- X{
- X char tch;
- X
- X if ((0 < point) && (point < length))
- X {
- X /* first, update the display */
- X
- X backspace(line[point]);
- X
- X echo(line[point]);
- X echo(line[point - 1]);
- X
- X /* now swap the chars in the line buffer */
- X
- X tch = line[point];
- X line[point] = line[point - 1];
- X line[point - 1] = tch;
- X
- X /* point moved forward one char */
- X
- X point++;
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Delete a character at point in the line buffer. While we are at it
- update the display to reflect the deletion.
- X*/
- X/*ARGSUSED*/
- void
- delete_char_under(ch)
- X char ch;
- X{
- X int i;
- X
- X if (point < length)
- X {
- X
- X /* clear to the end of the line */
- X
- X cleartoend();
- X
- X /* retype the rest of the line */
- X
- X echoline(&line[point + 1], (length - point - 1));
- X
- X /* build the new line */
- X
- X for (i = point + 1; i < length; i++)
- X {
- X line[i - 1] = line[i];
- X backspace(line[i]);
- X }
- X
- X length--;
- X
- X if (point > length)
- X {
- X point = length;
- X }
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Delete the character to the left of point in the line buffer. While we
- are at it update the display to reflect the deletion.
- X*/
- X/*ARGSUSED*/
- void
- delete_char(ch)
- X char ch;
- X{
- X int i;
- X
- X if (point > 0)
- X {
- X /* move the cursor left one character */
- X
- X backspace(line[point - 1]);
- X
- X /* clear to the end of the line */
- X
- X cleartoend();
- X
- X /* retype the rest of the line */
- X
- X echoline(&line[point], (length - point));
- X
- X /* build the new line */
- X
- X for (i = point; i < length; i++)
- X {
- X line[i - 1] = line[i];
- X backspace(line[i]);
- X }
- X
- X length--;
- X point--;
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Bind the edit vector to quote_edit so that the next character
- will be placed in the line buffer.
- X*/
- X/*ARGSUSED*/
- void
- quote(ch)
- X char ch;
- X{
- X edit = quote_edit;
- X}
- X/*------------------------------------------------------------------*/
- X/*
- The next character will select an action from action_table[1]
- X*/
- X/*ARGSUSED*/
- void
- escape_1(ch)
- X char ch;
- X{
- X edit = edit_1;
- X}
- X/*------------------------------------------------------------------*/
- X/*
- The next character will select an action from action_table[2]
- X*/
- X/*ARGSUSED*/
- void
- escape_2(ch)
- X char ch;
- X{
- X edit = edit_2;
- X}
- X/*------------------------------------------------------------------*/
- X/*
- The next character will select an action from action_table[3]
- X*/
- X/*ARGSUSED*/
- void
- escape_3(ch)
- X char ch;
- X{
- X edit = edit_3;
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Delete the word to the left of the cursor.
- X*/
- X/*ARGSUSED*/
- void
- delete_word(ch)
- X char ch;
- X{
- X int i;
- X int old;
- X
- X if (length > 0)
- X {
- X /* find the new deletion point */
- X
- X old = point;
- X
- X /* first skip over any delimiters */
- X
- X for (; (point > 0) && (delimit[line[point - 1]]); point--)
- X {
- X backspace(line[point - 1]);
- X }
- X
- X /* now delete until we find a delimiter */
- X
- X for (; (point > 0) && (!delimit[line[point - 1]]); point--)
- X {
- X backspace(line[point - 1]);
- X }
- X
- X /* clear to the end of the line */
- X
- X cleartoend();
- X
- X /* retype the rest of the line */
- X
- X echoline(&line[old], (length - old));
- X
- X /* construct the new line */
- X
- X for (i = 0; i < (length - old); i++)
- X {
- X line[point + i] = line[old + i];
- X backspace(line[point + i]);
- X }
- X
- X /* update the length */
- X
- X length = length - (old - point);
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Go forward one word.
- X*/
- X/*ARGSUSED*/
- void
- forward_word(ch)
- X char ch;
- X{
- X if (length > 0)
- X {
- X /* first skip any delimiters */
- X
- X for (; (point < length) && (delimit[line[point]]); point++)
- X {
- X echo(line[point]);
- X }
- X
- X /* now skip until we find a delimiter */
- X
- X for (; (point < length) && (!delimit[line[point]]); point++)
- X {
- X echo(line[point]);
- X }
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Lower case the word.
- X*/
- X/*ARGSUSED*/
- void
- lower_word(ch)
- X char ch;
- X{
- X if (length > 0)
- X {
- X /* first skip any delimiters */
- X
- X for (; (point < length) && (delimit[line[point]]); point++)
- X {
- X echo(line[point]);
- X }
- X
- X /* now skip until we find a delimiter */
- X
- X for (; (point < length) && (!delimit[line[point]]); point++)
- X {
- X if ((line[point] >= 'A') && (line[point] <= 'Z'))
- X {
- X line[point] = line[point] - 'A' + 'a';
- X echo(line[point]);
- X }
- X else
- X {
- X echo(line[point]);
- X }
- X }
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Upper case the word.
- X*/
- X/*ARGSUSED*/
- void
- upper_word(ch)
- X char ch;
- X{
- X if (length > 0)
- X {
- X /* first skip any delimiters */
- X
- X for (; (point < length) && (delimit[line[point]]); point++)
- X {
- X echo(line[point]);
- X }
- X
- X /* now skip until we find a delimiter */
- X
- X for (; (point < length) && (!delimit[line[point]]); point++)
- X {
- X if ((line[point] >= 'a') && (line[point] <= 'z'))
- X {
- X line[point] = line[point] - 'a' + 'A';
- X echo(line[point]);
- X }
- X else
- X {
- X echo(line[point]);
- X }
- X }
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Capitalize the word.
- X*/
- X/*ARGSUSED*/
- void
- capitalize_word(ch)
- X char ch;
- X{
- X if (length > 0)
- X {
- X /* first skip any delimiters */
- X
- X for (; (point < length) && (delimit[line[point]]); point++)
- X {
- X echo(line[point]);
- X }
- X
- X /* now skip until we find a delimiter */
- X
- X if ((point < length) && (!delimit[line[point]]))
- X {
- X if ((line[point] >= 'a') && (line[point] <= 'z'))
- X {
- X line[point] = line[point] - 'a' + 'A';
- X echo(line[point]);
- X }
- X else
- X {
- X echo(line[point]);
- X }
- X }
- X point++;
- X
- X for (; (point < length) && (!delimit[line[point]]); point++)
- X {
- X if ((line[point] >= 'A') && (line[point] <= 'Z'))
- X {
- X line[point] = line[point] - 'A' + 'a';
- X echo(line[point]);
- X }
- X else
- X {
- X echo(line[point]);
- X }
- X }
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Go backward one word.
- X*/
- X/*ARGSUSED*/
- void
- backward_word(ch)
- X char ch;
- X{
- X if (length > 0)
- X {
- X /* first backspace over any delimiters */
- X
- X for (; (point > 0) && (delimit[line[point - 1]]); point--)
- X {
- X backspace(line[point - 1]);
- X }
- X
- X /* now backspace until we find a delimiter */
- X
- X for (; (point > 0) && (!delimit[line[point - 1]]); point--)
- X {
- X backspace(line[point - 1]);
- X }
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Move the cursor to the start of the line.
- X*/
- X/*ARGSUSED*/
- void
- start_of_line(ch)
- X char ch;
- X{
- X int i;
- X
- X if (length > 0)
- X {
- X for (i = 0; i < point; i++)
- X {
- X backspace(line[i]);
- X }
- X point = 0;
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Move the cursor one character to the left.
- X*/
- X/*ARGSUSED*/
- void
- backward_char(ch)
- X char ch;
- X{
- X if ((length > 0) && (point > 0))
- X {
- X backspace(line[point - 1]);
- X point--;
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Move the cursor to the right of the last character on the line.
- X*/
- X/*ARGSUSED*/
- void
- end_of_line(ch)
- X char ch;
- X{
- X if ((length > 0) && (point < length))
- X {
- X echoline(&line[point], (length - point));
- X point = length;
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Move the cursor one character to the right.
- X*/
- X/*ARGSUSED*/
- void
- forward_char(ch)
- X char ch;
- X{
- X if ((length > 0) && (point < length))
- X {
- X echo(line[point]);
- X point++;
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Add a line to the history buffer and pass it to the child process
- as input.
- X*/
- X/*ARGSUSED*/
- void
- add_to_history(ch)
- X char ch;
- X{
- X /* Put the line in the history buffer. Make here point to the current
- X * line. And increment head to point to the next history slot. */
- X
- X /* If the current line is identical to the current history line, don't
- X * add it. */
- X
- X /* don't save blank lines */
- X
- X int prev;
- X
- X if ((head - 1) < 0)
- X {
- X prev = HISTORY_SIZE - 1;
- X }
- X else
- X {
- X prev = head - 1;
- X }
- X
- X if ((length != 0) &&
- X ((length != hist[prev].length) ||
- X (strncmp(hist[prev].line, line, length) != 0)))
- X {
- X /* set the length of the entry */
- X
- X hist[head].length = length;
- X
- X /* make sure there is enough storage for the new line */
- X
- X if (hist[head].line == NULL)
- X {
- X if ((hist[head].line = (char *) malloc((unsigned) length)) == NULL)
- X {
- X perror("ile");
- X }
- X }
- X else
- X {
- X if ((hist[head].line =
- X (char *) realloc(hist[head].line, (unsigned) length))
- X == NULL)
- X {
- X perror("ile");
- X }
- X }
- X
- X (void) strncpy(hist[head].line, line, length);
- X
- X head = (head + 1) % HISTORY_SIZE;
- X
- X if (hist[head].line != NULL)
- X {
- X free(hist[head].line);
- X hist[head].length = 0;
- X hist[head].line = NULL;
- X }
- X }
- X
- X /* reset here */
- X
- X here = head;
- X
- X /* Echo a carriage return or a newline as a cr-nl sequence. Then send the
- X * line to the child process. Finally, clear the buffer for reuse. */
- X
- X fputs(ccr, stdout);
- X fputs(cnl, stdout);
- X
- X line[length] = nl;
- X length++;
- X
- X write_line(line, length);
- X
- X point = 0;
- X length = 0;
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- XErase the entire line.
- X*/
- X/*ARGSUSED*/
- void
- erase_line(ch)
- X char ch;
- X{
- X /* remove any text from the display */
- X
- X clearline(ch);
- X
- X /* nothing in the line buffer */
- X
- X point = 0;
- X length = 0;
- X
- X /* reset here */
- X
- X here = head;
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- XErase from the current cursor position to the end of the line.
- X*/
- X/*ARGSUSED*/
- void
- erase_to_end_of_line(ch)
- X char ch;
- X{
- X if ((length > 0) && (point < length))
- X {
- X cleartoend();
- X length = point;
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Retype the current contents of the edit buffer.
- X*/
- X/*ARGSUSED*/
- void
- retype_line(ch)
- X char ch;
- X{
- X int i;
- X
- X fputs(ccr, stdout);
- X fputs(cnl, stdout);
- X
- X echoline(line, length);
- X
- X for (i = point; i < length; i++)
- X {
- X backspace(line[i]);
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Go to the the next entry in the history buffer and display it.
- If we are past the last history entry, then beep.
- X*/
- void
- forward_history(ch)
- X char ch;
- X{
- X if (here != head)
- X {
- X clearline(ch);
- X
- X here = (here + 1) % HISTORY_SIZE;
- X length = hist[here].length;
- X point = length;
- X
- X (void) strncpy(line, hist[here].line, length);
- X echoline(line, length);
- X }
- X else
- X {
- X bell('\0');
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Search backward in the history list for a line that starts with
- the characters left of the cursor. If it is found make it the
- current line.
- X*/
- void
- search_backward_history(ch)
- X char ch;
- X{
- X int prev;
- X int i;
- X
- X /* search backward in the history */
- X
- X prev = here;
- X
- X do
- X {
- X prev--;
- X
- X if (prev < 0)
- X {
- X prev = HISTORY_SIZE - 1;
- X }
- X }
- X while ((hist[prev].line != NULL) &&
- X (strncmp(line, hist[prev].line, point) != 0));
- X
- X /* if something was found, make it the current line */
- X
- X if (hist[prev].line != NULL)
- X {
- X /* remember the position in the history */
- X
- X here = prev;
- X
- X /* set the length and point correctly */
- X
- X length = hist[here].length;
- X if (point > length)
- X {
- X point = length;
- X }
- X
- X /* redraw the line */
- X
- X clearline(ch);
- X (void) strncpy(line, hist[here].line, hist[here].length);
- X echoline(line, length);
- X
- X for (i = point; i < length; i++)
- X {
- X backspace(line[i]);
- X }
- X }
- X else
- X {
- X bell('\0');
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Go back one entry in the history buffer and display it. If we are
- already at the last entry, then beep.
- X*/
- void
- backward_history(ch)
- X char ch;
- X{
- X int prev;
- X
- X prev = here - 1;
- X
- X if (prev < 0)
- X {
- X prev = HISTORY_SIZE - 1;
- X }
- X
- X if (hist[prev].line != NULL)
- X {
- X clearline(ch);
- X
- X here = prev;
- X length = hist[here].length;
- X point = length;
- X
- X (void) strncpy(line, hist[here].line, length);
- X echoline(line, length);
- X }
- X else
- X {
- X bell('\0');
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- The following routines are utility routines used by the editing
- routines.
- X*/
- X/*------------------------------------------------------------------*/
- X/*
- Clear to the end of the current input line.
- X*/
- void
- cleartoend()
- X{
- X /* send the clear character */
- X
- X fputs(cce, stdout);
- X
- X /* send somes nulls for padding */
- X
- X fputs("\0\0\0\0", stdout);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Clear the input line. Backspace to the start of the line. Then clear
- to the end of the line.
- X*/
- X/*ARGSUSED*/
- void
- clearline(ch)
- X char ch;
- X{
- X int i;
- X
- X for (i = 0; i < point; i++)
- X {
- X backspace(line[i]);
- X }
- X
- X cleartoend();
- X}
- X/*------------------------------------------------------------------*/
- X/*
- XEcho a character. Not all characters are created equal. Control characters
- are echoed in ^X form. So they take up two character positions instead of
- the normal 1 character position.
- X*/
- void
- echo(ch)
- X char ch;
- X{
- X /* how should we echo the char? */
- X
- X if (ch < ' ')
- X {
- X (void) fputc('^', stdout);
- X (void) fputc('@' + ch, stdout);
- X }
- X else
- X {
- X (void) fputc(ch, stdout);
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- XEcho a line. Print a whole line with control characters printed in
- X^X form.
- X*/
- void
- echoline(line, length)
- X char *line;
- X int length;
- X{
- X int i;
- X
- X for (i = 0; i < length; i++)
- X {
- X echo(*line++);
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Backspace over a character. Generate enough bs characters to backspace
- over any character.
- X*/
- void
- backspace(ch)
- X char ch;
- X{
- X if (ch < ' ')
- X {
- X fputs(cle, stdout);
- X fputs(cle, stdout);
- X }
- X else
- X {
- X fputs(cle, stdout);
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Add any character to the line buffer.
- X*/
- void
- quote_edit(ch)
- X char ch;
- X{
- X insert(ch);
- X
- X edit = edit_0;
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Given a character and an action table number either execute the
- action or pass the string to (*edit)(ch)
- X*/
- void
- dispatch(table, ch)
- X int table;
- X char ch;
- X{
- X char *cptr;
- X
- X switch (action_table[table][ch].flag)
- X {
- X case is_action:
- X
- X (*(action_table[table][ch].aors.action)) (ch);
- X
- X break;
- X
- X case is_string:
- X
- X cptr = action_table[table][ch].aors.string;
- X while ((*cptr) != '\0')
- X {
- X (*edit) (*cptr);
- X cptr++;
- X }
- X
- X break;
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Select an action from action_table[3] and execute it.
- X*/
- void
- edit_3(ch)
- X char ch;
- X{
- X /* reset so that next input is handled by edit_0 unless over ridden by
- X * the action. */
- X
- X edit = edit_0;
- X dispatch(3, ch);
- X (void) fflush(stdout);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Select an action from action_table[2] and execute it.
- X*/
- void
- edit_2(ch)
- X char ch;
- X{
- X /* reset so that next input is handled by edit_0 unless over ridden by
- X * the action. */
- X
- X edit = edit_0;
- X dispatch(2, ch);
- X (void) fflush(stdout);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Select an action from action_table[1] and execute it.
- X*/
- void
- edit_1(ch)
- X char ch;
- X{
- X /* reset so that next input is handled by edit_0 unless over ridden by
- X * the action. */
- X
- X edit = edit_0;
- X dispatch(1, ch);
- X (void) fflush(stdout);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Select an action from action_table[0] and execute it.
- X*/
- void
- edit_0(ch)
- X char ch;
- X{
- X dispatch(0, ch);
- X (void) fflush(stdout);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Input line editor.
- X
- Initialize the world. Then loop forever using select to wait for
- characters to be available from either stdin or from master_pty.
- When characters are available, pass them on after doing any needed
- editing.
- X*/
- void
- ile()
- X{
- X /* general purpose integer variable */
- X
- X int i;
- X
- X /* arguments for read and write calls */
- X
- X char buffer[BUFFER_SIZE];
- X int cc;
- X
- X /* current slave_tty parameters */
- X
- X struct sgttyb slave_params;
- X
- X /* Arguments for select call */
- X
- X int nfds;
- X int width;
- X int readfds;
- X
- X /* what to do if the child or parent dies */
- X
- X (void) signal(SIGCHLD, clean_up);
- X (void) signal(SIGSEGV, clean_up);
- X (void) signal(SIGBUS, clean_up);
- X (void) signal(SIGTERM, clean_up);
- X (void) signal(SIGHUP, clean_up);
- X (void) signal(SIGINT, clean_up);
- X (void) signal(SIGQUIT, clean_up);
- X
- X /* what to do it the window changes size */
- X
- X (void) signal(SIGWINCH, change_window);
- X
- X /* Get all the different pieces of the current ttys' state and copy them
- X * to the slave_tty. */
- X
- X /* tty sgttyb */
- X
- X (void) ioctl(READ, TIOCGETP, &tty_sgttyb);
- X (void) ioctl(slave_tty, TIOCSETP, &tty_sgttyb);
- X
- X /* tty line discipline */
- X
- X (void) ioctl(READ, TIOCGETD, &tty_ldisc);
- X (void) ioctl(slave_tty, TIOCSETD, &tty_ldisc);
- X
- X /* tty tchars */
- X
- X (void) ioctl(READ, TIOCGETC, &tty_tchars);
- X (void) ioctl(slave_tty, TIOCSETC, &tty_tchars);
- X
- X /* tty mode */
- X
- X (void) ioctl(READ, TIOCLGET, &tty_mode);
- X (void) ioctl(slave_tty, TIOCLSET, &tty_mode);
- X
- X /* tty ltchars */
- X
- X (void) ioctl(READ, TIOCGLTC, &tty_ltchars);
- X (void) ioctl(slave_tty, TIOCSLTC, &tty_ltchars);
- X
- X /* tty windsize */
- X
- X (void) ioctl(READ, TIOCGWINSZ, &tty_winsize);
- X (void) ioctl(slave_tty, TIOCSWINSZ, &tty_winsize);
- X windowchanged = FALSE;
- X
- X /* "login" the user */
- X
- X (void) setutmp(slave_tty, TRUE);
- X
- X /* set raw mode on tty */
- X {
- X struct sgttyb params;
- X struct tchars tparams;
- X struct ltchars ltparams;
- X
- X /* Simulate RAW but allow original parity to work. Thus we use
- X * CBREAK with all the options turned off. */
- X
- X params = tty_sgttyb;
- X params.sg_flags = CBREAK;
- X (void) ioctl(READ, TIOCSETP, ¶ms);
- X
- X tparams = tty_tchars;
- X tparams.t_intrc = -1;
- X tparams.t_quitc = -1;
- X tparams.t_startc = -1;
- X tparams.t_stopc = -1;
- X tparams.t_eofc = -1;
- X tparams.t_brkc = -1;
- X (void) ioctl(READ, TIOCSETC, &tparams);
- X
- X ltparams = tty_ltchars;
- X ltparams.t_suspc = -1;
- X ltparams.t_dsuspc = -1;
- X ltparams.t_rprntc = -1;
- X ltparams.t_flushc = -1;
- X ltparams.t_lnextc = -1;
- X (void) ioctl(READ, TIOCSLTC, <params);
- X }
- X
- X /* set new mode on tty */
- X
- X {
- X int mode;
- X mode = LNOFLSH | LDECCTQ | LLITOUT;
- X (void) ioctl(READ, TIOCLSET, &mode);
- X }
- X
- X /* get descriptor table size */
- X
- X width = getdtablesize();
- X if (width > 32)
- X {
- X width = 32;
- X }
- X
- X /* set initial edit function */
- X
- X edit = edit_0;
- X
- X /* initialize line buffer */
- X
- X point = 0;
- X length = 0;
- X
- X /* initialize history buffer */
- X
- X head = 0;
- X here = 0;
- X
- X for (i = 0; i < HISTORY_SIZE; i++)
- X {
- X hist[i].length = 0;
- X hist[i].line = NULL;
- X }
- X
- X for (;;)
- X {
- X readfds = (1 << READ) | (1 << master_pty);
- X
- X /* wait for input from stdin or master_pty */
- X
- X nfds = select(width,
- X (fd_set *) & readfds,
- X (fd_set *) NULL,
- X (fd_set *) NULL,
- X (struct timeval *) NULL);
- X
- X if (nfds == -1) /* an exception has occured */
- X {
- X if (windowchanged)
- X {
- X /* nothing serious, the window changed size */
- X
- X windowchanged = FALSE;
- X }
- X else
- X {
- X perror("ile");
- X clean_up();
- X }
- X }
- X else if ((nfds > 0) && (readfds != 0)) /* something to read */
- X {
- X if ((readfds & (1 << master_pty)) != 0)
- X {
- X /* read the pending characters. */
- X
- X cc = read(master_pty, buffer, BUFFER_SIZE);
- X
- X /* display the characters. */
- X
- X (void) write(WRITE, buffer, cc);
- X }
- X
- X if ((readfds & (1 << READ)) != 0)
- X {
- X /* read the pending characters. */
- X
- X cc = read(READ, buffer, BUFFER_SIZE);
- X
- X /* if the slave is in RAW or CBREAK mode, or has turned off
- X * ECHO then we should not mess with its input characters */
- X
- X (void) ioctl(slave_tty, TIOCGETP, &slave_params);
- X
- X if (((slave_params.sg_flags & (RAW | CBREAK)) != 0) ||
- X (slave_params.sg_flags & ECHO) == 0)
- X {
- X edit = pass;
- X }
- X else if (edit == (void (*) ()) pass)
- X {
- X edit = edit_0;
- X }
- X
- X /* decide what to do with the characters. */
- X
- X for (i = 0; i < cc; i++)
- X {
- X (*edit) (CHAR_MASK & buffer[i]);
- X }
- X }
- X
- X }
- X }
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- The child process.
- X
- Make the pty the processes controling terminal. Bind the pty to
- stdin, stdout, and stderr. Then exec the users program.
- X*/
- void
- child(argv)
- X char *argv[];
- X{
- X /* shell name pointers */
- X
- X char *shellname;
- X char *shellpath;
- X char *dashshellname;
- X
- X /* close all file descriptors */
- X
- X (void) close(READ);
- X (void) close(WRITE);
- X (void) close(ERROR);
- X (void) close(slave_tty);
- X
- X /* get rid of controlling terminal */
- X
- X {
- X int tty;
- X
- X if ((tty = open("/dev/tty", O_RDWR) == -1) ||
- X (ioctl(0, TIOCNOTTY, 0) == -1) ||
- X (close(tty) == -1))
- X {
- X perror("ile");
- X }
- X }
- X
- X /* open the tty again */
- X /* this makes the pty the controlling terminal */
- X
- X if ((slave_tty = open(ttydev, O_RDWR)) == -1)
- X {
- X perror("ile");
- X }
- X
- X /* slave_tty is now stdin */
- X
- X /* bind slave_tty to stdout */
- X
- X (void) dup2(slave_tty, WRITE);
- X
- X /* bind slave_tty to stderr */
- X
- X (void) dup2(slave_tty, ERROR);
- X
- X /* close master_pty descriptor */
- X
- X (void) close(master_pty);
- X
- X /* Fire up application program. If no program name is given then fire up
- X * users favorite shell. */
- X
- X /* get the name of the users shell. default to /bin/csh */
- X
- X if ((shellpath = getenv("SHELL")) == NULL ||
- X (*shellpath == '\0'))
- X {
- X shellpath = "/bin/csh";
- X }
- X
- X /* get just the name */
- X
- X if ((shellname = strrchr(shellpath, '/')) != NULL)
- X {
- X shellname += sizeof(char);
- X }
- X
- X /* if the current argv[0] starts with -, then the new argv[0] */
- X /* should start with - */
- X
- X if (*(argv[0]) == '-')
- X {
- X dashshellname = (char *) malloc((unsigned) strlen(shellname) + 2);
- X (void) strcpy(dashshellname, "-");
- X (void) strcat(dashshellname, shellpath);
- X }
- X else
- X {
- X dashshellname = shellname;
- X }
- X
- X /* execute the shell or the specified program */
- X
- X if (argv[1] == NULL)
- X {
- X /* execute default shell */
- X
- X execlp(shellpath, dashshellname, 0);
- X }
- X else if (*argv[1] == '-')
- X {
- X /* there is an initialization file */
- X
- X if (argv[2] == NULL)
- X {
- X /* execute default shell */
- X
- X execlp(shellpath, dashshellname, 0);
- X }
- X else
- X {
- X /* execute specified program */
- X
- X execvp(argv[2], &argv[2]);
- X }
- X }
- X else
- X {
- X /* execute specified program */
- X
- X execvp(argv[1], &argv[1]);
- X }
- X
- X /* this executes if exec fails */
- X
- X perror("ile");
- X exit(1);
- X /* NOTREACHED */
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Set up default key bindings and delimeters.
- X*/
- void
- default_bindings()
- X{
- X int i;
- X
- X /* clear delimiter vector and the action table */
- X
- X for (i = 0; i < CHAR_SET_SIZE; i++)
- X {
- X delimit[i] = FALSE;
- X
- X action_table[0][i].aors.action = insert;
- X action_table[1][i].aors.action = bell;
- X action_table[2][i].aors.action = bell;
- X action_table[3][i].aors.action = bell;
- X
- X action_table[0][i].flag = is_action;
- X action_table[1][i].flag = is_action;
- X action_table[2][i].flag = is_action;
- X action_table[3][i].flag = is_action;
- X }
- X
- X /* default delimiters */
- X
- X delimit[' '] = TRUE; /* blank */
- X delimit['/'] = TRUE; /* slash */
- X delimit['.'] = TRUE; /* dot */
- X delimit['-'] = TRUE; /* dash */
- X
- X /* default action_table[0] */
- X
- X action_table[0][CA].aors.action = start_of_line;
- X action_table[0][CB].aors.action = backward_char;
- X action_table[0][CE].aors.action = end_of_line;
- X action_table[0][CF].aors.action = forward_char;
- X action_table[0][CK].aors.action = erase_to_end_of_line;
- X action_table[0][CU].aors.action = erase_line;
- X action_table[0][CL].aors.action = retype_line;
- X action_table[0][CN].aors.action = forward_history;
- X action_table[0][CP].aors.action = backward_history;
- X action_table[0][CR].aors.action = search_backward_history;
- X action_table[0][CT].aors.action = transpose_chars;
- X action_table[0][CV].aors.action = quote;
- X action_table[0][del].aors.action = delete_char;
- X action_table[0][esc].aors.action = escape_1;
- X action_table[0][cr].aors.action = add_to_history;
- X action_table[0][nl].aors.action = add_to_history;
- X action_table[0][CX].aors.action = delete_char_under;
- X
- X action_table[0][CC].aors.action = pass;
- X action_table[0][CD].aors.action = pass;
- X action_table[0][CQ].aors.action = pass;
- X action_table[0][CS].aors.action = pass;
- X action_table[0][CZ].aors.action = pass;
- X
- X /* default action_table[1] ^[ c */
- X
- X action_table[1]['b'].aors.action = backward_word;
- X action_table[1]['f'].aors.action = forward_word;
- X action_table[1][del].aors.action = delete_word;
- X action_table[1]['u'].aors.action = upper_word;
- X action_table[1]['l'].aors.action = lower_word;
- X action_table[1]['c'].aors.action = capitalize_word;
- X action_table[1]['['].aors.action = escape_2;
- X action_table[1][esc].aors.action = complete_file;
- X action_table[1]['s'].aors.action = complete_file_full;
- X action_table[1]['d'].aors.action = show_files;
- X action_table[1]['p'].aors.action = query_path;
- X
- X /* default action_table[2] ^[ [ */
- X
- X action_table[2]['A'].aors.action = backward_history;
- X action_table[2]['B'].aors.action = forward_history;
- X action_table[2]['C'].aors.action = forward_char;
- X action_table[2]['D'].aors.action = backward_char;
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Return a character or EOF. This routine reads characters from input
- and converts them into a character using the following rules.
- X
- The character may be a single character, a control
- character indicated by ^x, an octal number starting with \, or an
- escaped character indictated by \x.
- X*/
- int
- scan_char(input)
- X FILE *input;
- X{
- X int ch;
- X int value;
- X
- X ch = fgetc(input);
- X switch (ch)
- X {
- X case '^':
- X
- X /* it is a control character */
- X
- X for (ch = fgetc(input); '@' <= ch; ch = ch - '@');
- X
- X break;
- X
- X case '\\':
- X
- X /* octal or an escaped character? */
- X
- X ch = fgetc(input);
- X if (('0' <= ch) && (ch <= '7'))
- X {
- X
- X /* its an octal number */
- X
- X value = 0;
- X while (('0' <= ch) && (ch <= '7'))
- X {
- X value = (value * 8) + (ch - '0');
- X ch = fgetc(input);
- X }
- X (void) ungetc(ch, input);
- X
- X ch = value & 0177; /* make sure it is in range */
- X }
- X else
- X {
- X /* its an escaped character */
- X
- X ch = fgetc(input);
- X }
- X
- X break;
- X
- X case '\n':
- X
- X /* the real end of the line */
- X
- X ch = EOL;
- X
- X break;
- X
- X default:
- X
- X /* it is just itself */
- X
- X break;
- X }
- X
- X return (ch);
- X
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Set key bindings and delimiters from the users file.
- X*/
- void
- user_bindings(file)
- X FILE *file;
- X{
- X
- X#define NAME_SIZE 40
- X
- X static struct action_name_table
- X {
- X char *name;
- X void (*action) ();
- X } action_name_table[] =
- X {
- X {
- X "complete_file_full", complete_file_full
- X },
- X {
- X "complete_file", complete_file
- X },
- X {
- X "show_files", show_files
- X },
- X {
- X "query_path", query_path
- X },
- X {
- X "bell", bell
- X },
- X {
- X "pass", pass
- X },
- X {
- X "insert", insert
- X },
- X {
- X "transpose_chars", transpose_chars
- X },
- X {
- X "delete_char", delete_char
- X },
- X {
- X "delete_char_under", delete_char_under
- X },
- X {
- X "quote", quote
- X },
- X {
- X "escape_1", escape_1
- X },
- X {
- X "escape_2", escape_2
- X },
- X {
- X "escape_3", escape_3
- X },
- X {
- X "delete_word", delete_word
- X },
- X {
- X "upper_word", upper_word
- X },
- X {
- X "lower_word", lower_word
- X },
- X {
- X "capitalize_word", capitalize_word
- X },
- X {
- X "forward_word", forward_word
- X },
- X {
- X "backward_word", backward_word
- X },
- X {
- X "start_of_line", start_of_line
- X },
- X {
- X "backward_char", backward_char
- X },
- X {
- X "end_of_line", end_of_line
- X },
- X {
- X "forward_char", forward_char
- X },
- X {
- X "add_to_history", add_to_history
- X },
- X {
- X "erase_line", erase_line
- X },
- X {
- X "erase_to_end_of_line", erase_to_end_of_line
- X },
- X {
- X "retype_line", retype_line
- X },
- X {
- X "forward_history", forward_history
- X },
- X {
- X "backward_history", backward_history
- X },
- X {
- X "search_backward_history", search_backward_history
- X },
- X {
- X "", NULL
- X }
- X };
- X
- X char name[NAME_SIZE];
- X
- X int ch;
- X int i;
- X
- X int linecount;
- X int table;
- X int entry;
- X
- X /* First clear the default delimiters */
- X
- X for (i = 0; i < CHAR_SET_SIZE; i++)
- X {
- X delimit[i] = FALSE;
- X }
- X
- X /* Now read the delimiter characters */
- X
- X while (((int) (ch = fgetc(file)) != EOF) && (ch != '\n'))
- X {
- X delimit[ch] = TRUE;
- X }
- X
- X linecount = 2;
- X
- X /* Now read the character binding pairs */
- X
- X while ((int) (ch = fgetc(file)) != EOF)
- X {
- X switch (ch)
- X {
- X case '\n':
- X
- X /* skipping a blank line */
- X linecount++;
- X
- X break;
- X
- X case '0':
- X case '1':
- X case '2':
- X case '3':
- X
- X /* which table is this entry directed to? */
- X
- X table = ch - '0';
- X
- X /* get the character code */
- X
- X entry = scan_char(file);
- X
- X /* make sure the '=' is there */
- X
- X ch = fgetc(file);
- X if (ch != '=')
- X {
- X (void) fprintf(stderr,
- X "ile: '=' missing on line %d\n",
- X linecount);
- X exit(1);
- X /* NOTREACHED */
- X }
- X
- X /* collect the action name or string */
- X
- X for (ch = scan_char(file), i = 0;
- X ((int) ch != EOL) && (i < (NAME_SIZE - 1));
- X ch = scan_char(file), i++)
- X {
- X name[i] = ch;
- X name[i + 1] = '\0';
- X }
- X
- X /* look it up in the action_name_table */
- X
- X for (i = 0;
- X (action_name_table[i].action != NULL) &&
- X (strcmp(name, action_name_table[i].name) != 0);
- X i++);
- X
- X /* if it was found, put it in the action array */
- X
- X if (action_name_table[i].action == NULL)
- X {
- X /* must be a string */
- X
- X action_table[table][entry].flag = is_string;
- X action_table[table][entry].aors.string =
- X (char *) malloc((unsigned) strlen(name) + 1);
- X (void) strcpy(action_table[table][entry].aors.string, name);
- X }
- X else
- X {
- X /* its an action */
- X
- X action_table[table][entry].flag = is_action;
- X action_table[table][entry].aors.action =
- X action_name_table[i].action;
- X }
- X
- X linecount++; /* count the line */
- X
- X break;
- X
- X default:
- X (void) fprintf(stderr,
- X "\nile: error in initialization file on line %d\n",
- X linecount);
- X exit(1);
- X /* NOTREACHED */
- X }
- X }
- X
- X (void) fclose(file);
- X}
- X/*------------------------------------------------------------------*/
- X/*
- Initialize key bindings and delimiters.
- X*/
- void
- initialize(argv)
- X char *argv[];
- X{
- X FILE *file;
- X char name[BUFFER_SIZE];
- X char *pwd;
- X
- X /* set up the default bindings */
- X
- X default_bindings();
- X
- X /* Look for an initialization file. If it's there, load it. */
- X
- X name[0] = '\0';
- X homedir = getenv("HOME");
- X if (homedir == NULL)
- X {
- X /* no home dir, use / instead */
- X name[0] = '\0';
- X }
- X else
- X {
- X (void) strcpy(name, homedir);
- X }
- X (void) strcat(name, "/.ilerc");
- X
- X /* initialize currentdir */
- X
- X pwd = getenv("PWD");
- X if (pwd == NULL)
- X {
- X /* no pwd, use homedir instead */
- X (void) strcpy(currentdir, homedir);
- X }
- X else
- X {
- X (void) strcpy(currentdir, pwd);
- X }
- X
- X if ((argv[1] != NULL) &&
- X (*argv[1] == '-') &&
- X ((file = fopen(argv[1] + 1, "r")) != NULL))
- X {
- X /* load the users bindings */
- X
- X user_bindings(file);
- X }
- X else if (((file = fopen("./.ilerc", "r")) != NULL) ||
- X ((file = fopen(name, "r")) != NULL))
- X {
- X user_bindings(file);
- X }
- X}
- X/*------------------------------------------------------------------*/
- X/*
- X*/
- X/*ARGSUSED*/
- main(argc, argv)
- X int argc;
- X char *argv[];
- X{
- X
- X /* Child process id */
- X
- X int childpid;
- X
- X /* identify yourself */
- X
- X (void) fprintf(stdout, "ile rev.2\n\r");
- X
- X /* create the tty/pty pair */
- X
- X getpty(&master_pty, &slave_tty);
- X
- X /* get control sequences from termcap */
- X
- X get_termcap();
- X
- X /* initialize the dispatch vectors */
- X
- X initialize(argv);
- X
- X /* create the child process */
- X
- X childpid = fork();
- X
- X switch (childpid)
- X {
- X case 0: /* child process */
- X
- X child(argv);
- X break;
- X
- X case -1: /* fork failed */
- X
- X perror("ile");
- X exit(1);
- X /* NOTREACHED */
- X
- X default: /* parent process */
- X ile();
- X break;
- X }
- X
- X}
- END_OF_FILE
- if test 60367 -ne `wc -c <'ile.c'`; then
- echo shar: \"'ile.c'\" unpacked with wrong size!
- fi
- # end of 'ile.c'
- fi
- echo shar: End of archive 1 \(of 1\).
- cp /dev/null ark1isdone
- MISSING=""
- for I in 1 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have the archive.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-